﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace DienLib
{
    public class TreeBase<T>
    {
        ItemCollection<T> items;
        public ItemCollection<T> Items
        {
            get { return items; }
            set { items = value; }
        }

        public TreeBase()
        {
            items = new ItemCollection<T>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="FileOrFolderPath"></param>
        /// <param name="SearchPattern">Search Pattern in Regex Format</param>
        /// <returns></returns>
        public static TreeBase<FileSystemInfo> ExploreFolder(string FileOrFolderPath, string SearchPattern)
        {
            // CHECK INIT CONDITION
            DirectoryInfo folder;
            if (File.Exists(FileOrFolderPath)) folder = new FileInfo(FileOrFolderPath).Directory;
            else if (Directory.Exists(FileOrFolderPath)) folder = new DirectoryInfo(FileOrFolderPath);
            else throw new InvalidOperationException("ExploreFolder : No File Or Folder Exist with this Path !");
            
            // CREATE NEW TREE WITH PARENT FOLDER AS FIRST NODE
            TreeBase<FileSystemInfo> tree = new TreeBase<FileSystemInfo>();
            tree.Items.Add(new TreeItem<FileSystemInfo>(folder)); // Init first node for Tree
            Regex regex = new Regex(SearchPattern, RegexOptions.IgnoreCase); // & Start Regex Pattern
            
            ExploreFolder(folder, tree.Items[0], regex); // Then Start Recursion Loop

            // GET ALL FOLDERS first : //http://stackoverflow.com/questions/7039580/multiple-file-extensions-searchpattern-for-system-io-directory-getfiles
            //var folders = Directory.GetDirectories(FolderPath);
            // GET ALL FILES later : http://stackoverflow.com/questions/3527203/getfiles-with-multiple-extentions
            //var files = Directory.GetFiles(FolderPath).Where(f => regex.IsMatch(f.Substring(f.LastIndexOf('\\'))));
            
            return tree;
        }

        /// <summary>
        /// This method for Recursion use only. Need to prepare regex, folder & item for it first
        /// </summary>
        /// <param name="d"></param>
        /// <param name="item"></param>
        /// <param name="regex"></param>
        public static void ExploreFolder(DirectoryInfo folder, TreeItem<FileSystemInfo> treeitem, Regex regex)
        {
            //create an array of files using FileInfo object
            FileInfo[] files = folder.GetFiles("*.*"); // avoid blank extension file

            //iterate through the directory and print the files
            foreach (FileInfo file in files)
            {
                if (regex.IsMatch(file.Name)) treeitem.Items.Add(new TreeItem<FileSystemInfo>(file));
                // else
            }

            //get sub-folders for the current directory
            DirectoryInfo[] dirs = folder.GetDirectories("*.*");
            
            //This is the code that calls 
            //the getDirsFiles (calls itself recursively)
            //This is also the stopping point 
            //(End Condition) for this recursion function 
            //as it loops through until 
            //reaches the child folder and then stops.
            foreach (DirectoryInfo dir in dirs)
            {
                treeitem.Items.Add(new TreeItem<FileSystemInfo>(dir));
                ExploreFolder(dir, treeitem.Items.Last(), regex); // Recursion Call Here.
            }

        }
    }

    public class ItemCollection<T> : IEnumerable<TreeItem<T>> //, IEnumerator<T>
    {
        List<TreeItem<T>> items;

        public ItemCollection()
        {
            items = new List<TreeItem<T>>();
        }

        public void Add(TreeItem<T> Item)
        {
            items.Add(Item);
        }

        public void Remove(TreeItem<T> Item)
        {
            items.Remove(Item);
        }

        public void Clear()
        {
            items.Clear();
        }

        /*public TreeItem<T> First()
        {
            return items.First();
        }

        public TreeItem<T> Last()
        {
            return items.Last();
        }*/

        
        
        public TreeItem<T> this[int index]
        {
            get { return items[index]; }
            set { items[index] = value; }
        }



        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        public IEnumerator<TreeItem<T>> GetEnumerator()
        {
            return items.GetEnumerator(); // Or Implement FULL & FLEXIBLE as below code with add IEnumerator<T> on Inheritance | Or can create a new Enumerator<T> Struct inside this class as what List do
        }

        /*public IEnumerator<T> GetEnumerator()
        {
            return this;
        }
        private int currentIndex = -1;
        public TreeItem<T> Current { get; private set; }
        public bool MoveNext()
        {
            if (++currentIndex >= items.Count) return false; else Current = items[currentIndex];
            return true;
        }
        public void Reset() { currentIndex = -1; }
        
        // Old Version Support
        object System.Collections.IEnumerator.Current { get { throw new NotImplementedException(); } }
        public void Dispose() { }*/
    }

    public class TreeItem<T>
    {
        ItemCollection<T> items;
        public ItemCollection<T> Items
        {
            get { return items; }
            set { items = value; }
        }

        public TreeItem(T value)
        {
            items = new ItemCollection<T>();
            Value = value;
        }

        public T Value { get; set; }


    }
}